package lock;

import org.junit.jupiter.api.Test;

import javax.xml.crypto.Data;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockClass {
    /**
     * 锁的分类
     *
     *     线程要不要锁住同步资源
     *     锁住 悲观锁  synchronized lock
     *     不锁住 乐观锁 并发原子类
     *
     *     乐观锁在访问共享资源时不上锁，会比较第一次拿到和第二次拿到的结果，两次相同写入生效，不同再次执行之前相同的操作，避免了线程阻塞唤醒的耗时操作，
     *     但是乐观锁随着并发数的增加，也会出现颓势；
     *
     *     乐观锁：适用于并发写入少，大部分是读取的场景，不加锁能让读取的性能大幅度提高
     *     悲观锁：适合并发写入多的情况，适用于临界区持锁时间比较长的情况，悲观锁可以避免大量的无用自旋消耗，典型情况：
     *     1.临界区有IO操作
     *     2.临界区代码复杂或者循环量大
     *     3.临界区竞争非常激烈（竞争的线程多）
     *
     *
     *
     *     多线程能否共享一把锁
     *      可以 共享锁
     *      不可以 独占锁
     *
     *      多线程竞争时，是否排队
     *      排队 公平锁
     *      先尝试插队，插队失败在排队 非公平锁,避免唤醒带来的空档期
     *      ReentrantLock 默认非公平锁，但是可以设置
     *
     *      同一线程是否可以重复获取同一把锁
     *      可以 可重入锁 ReentrantLock
     *      不可以 不可重入锁
     *
     *      是否可以中断
     *      可以 可中断锁
     *      不可以 不可中断锁
     *
     *      等锁过程
     *      自旋 自旋锁
     *      阻塞 非自旋锁
     */

    /*
    读写锁 观察Test 线程执行情况
     */
    public static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
    private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

    @Test
    public void test(){
        new Thread(()->read(),"Thread1").start();
        new Thread(()->read(),"Thread2").start();
    }

    @Test
    public void test2() throws InterruptedException {
        new Thread(()->write(),"Thread1").start();
        new Thread(()->read(),"Thread2").start();
        Thread.sleep(5000);
    }


    @Test
    public void test3() throws InterruptedException {
        new Thread(()->read(),"Thread1").start();
        new Thread(()->write(),"Thread2").start();
        Thread.sleep(5000);
    }


    /**
     * 模拟出来 写锁插队的场景
     *
     * @throws InterruptedException
     */
    @Test
    void nofairTest1() throws InterruptedException {

        new Thread(()->write(), "线程一").start();
        Thread.sleep(5);
        new Thread(()->read(), "线程二").start();
        Thread.sleep(5);
        new Thread(()->read(), "线程三").start();
        Thread.sleep(5);
        new Thread(()->write(), "线程四").start();
        Thread.sleep(5);
        new Thread(()->read(), "线程五").start();
        Thread.sleep(5);
        new Thread(()->read(), "线程六").start();
        new Thread(() -> {
            for (int i = 0; i < 800; i++) {
                new Thread(()->write(), "线程" + i).start();
            }
        }).start();


        TimeUnit.SECONDS.sleep(50);
    }


    public void read() {
        System.out.println(Thread.currentThread().getName() + "尝试获取读锁");
        readLock.lock();
        try {
            TimeUnit.MILLISECONDS.sleep(20);
            System.out.println(Thread.currentThread().getName() + " 获取到了读锁...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
        }
    }

    public void write() {
        System.out.println(Thread.currentThread().getName() + "尝试获取写锁");
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获取到了写锁..."+"时间"+LocalDateTime.now());
            TimeUnit.MILLISECONDS.sleep(45);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " 释放到了写锁...");
            writeLock.unlock();
        }
    }












}
